/*
 * Copyright 2010, 2011 by the University of Richmond, Richmond, VA 23173
 * Written by Eric F. Palmer eric@ericfpalmer.com || epalmer@richmond.edu
 * And other contributors
 * http://www.richmond.edu/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Project files reside at http://code.google.com/p/files-2-exist-db
 */

/*
 * this class just sets up configuration values for the application
 *
 * Instantiate this class only once
 * @version 1.0
 * @author epalmer
 */

package edu.richmond.is.webservices.files2exist;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//TODO uncomment below
//import edu.richmond.is.webservices.files2exist.transforms.GenericTransform;
import org.apache.log4j.Logger;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class AppConfig {
        //private ArrayList<String> dirList = new ArrayList<String>(20);

    private String logFileDir;
    private String appName;
    private long fileChangeSleepTime;
    private long applicationSleepTime;
    private long heartbeatLoopCounter;
    private List<Folder2ExistSettings> folderSettingsArrayList;
    private List<SrcExistVO> subfolders;

    private static Logger logger = Logger.getLogger(ConfigConstants.LOGGER_GENERAL);

    public AppConfig(String cmdLineConfigFileLoc) {
        boolean successConfigLoad = readConfig(cmdLineConfigFileLoc);
        if(successConfigLoad){
            initFolderSettings();
        }
    }

    private boolean readConfig(String cmdLineConfigFileLoc) throws DOMException, NumberFormatException {
        String configFileName = ConfigConstants.DEFAULT_CONFIG_FILE;
        Document configFile = null;
        subfolders = new ArrayList<SrcExistVO>();

        //If a file name has been passed in from the command line,
        //use that value instead of the default defined in ConfigConstants
        if(cmdLineConfigFileLoc != null){
            configFileName = cmdLineConfigFileLoc;
        }

        try {
            /*
             * Get the default f2x config file, load it and parse into a
             * Document
             */
            File file = new File(configFileName);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            configFile = db.parse(file);
            configFile.getDocumentElement().normalize();

            /**
             *  Read data from XML file and store
             * values in class variables
             */
            if (configFile != null) {

                //App Name
                Element appNameNode = (Element) configFile.getElementsByTagName(ConfigConstants.APP_NAME).item(0);
                appName = appNameNode.getChildNodes().item(0).getNodeValue();

                //File Change Sleep Time
                Element fileChangeSleepTimeNode = (Element) configFile.getElementsByTagName(ConfigConstants.FILE_CHANGED_MILLI).item(0);
                String fileChangeSleepTimeStr = fileChangeSleepTimeNode.getChildNodes().item(0).getNodeValue();
                fileChangeSleepTime = Long.parseLong(fileChangeSleepTimeStr.trim());

                //Application Sleep Time
                Element applicationSleepTimeNode = (Element) configFile.getElementsByTagName(ConfigConstants.APP_SLEEP_MILLI).item(0);
                String applicationSleepTimeStr = applicationSleepTimeNode.getChildNodes().item(0).getNodeValue();
                applicationSleepTime = Long.parseLong(applicationSleepTimeStr.trim());

                //Heartbeat Loop
                Element heartbeatLoopCounterNode = (Element) configFile.getElementsByTagName(ConfigConstants.HEARTBEAT_COUNTER).item(0);
                String heartbeatLoopCounterStr = heartbeatLoopCounterNode.getChildNodes().item(0).getNodeValue();
                heartbeatLoopCounter = Long.parseLong(heartbeatLoopCounterStr.trim());


                //Subfolder paths for Cascade/eXist
                NodeList allPathElms = configFile.getElementsByTagName(ConfigConstants.PATH_ELMS);
                for (int i = 0; i < allPathElms.getLength(); i++) {

                    Node singlePathElm = allPathElms.item(i);

                    if (singlePathElm.getNodeType() == Node.ELEMENT_NODE) {
                        Element element = (Element) singlePathElm;

                        //create/populate CMS/Exist value object
                        //add to subfolder list
                        SrcExistVO vo = new SrcExistVO();
                        vo.setCms(getElementValue(element, ConfigConstants.PATH_CMS));
                        //
                        // Recurse is true unless any case of false 
                        // is explicitly configured in PATH_RECURSE
                        //
                        vo.setRecurse(!"false".equalsIgnoreCase(getElementValue(element, ConfigConstants.PATH_RECURSE)));
                        vo.setExist(getElementValue(element, ConfigConstants.PATH_EXIST));
                        vo.setServer(getElementValue(element, ConfigConstants.SERVER));
                        vo.setPort(Integer.parseInt(getElementValue(element, ConfigConstants.PORT)));
                        vo.setUseSSL(Boolean.parseBoolean(getElementValue(element, ConfigConstants.SSL)));
                        vo.setHttpMethod(getElementValue(element, ConfigConstants.HTTP_METHOD));
                        vo.setUname(getElementValue(element, ConfigConstants.USER_NAME));
                        vo.setPword(getElementValue(element, ConfigConstants.PASSWORD));


                        // these are optional
                        // one TYPE defined right now - no error handling on this however
                        // "XML"

                        try {
                            vo.setTransformType(getElementValue(element, ConfigConstants.TRANSFORM_TYPE));

                        } catch (Exception e) {
                            vo.setTransformType("NA");
                        }
                        logger.debug("TRANSFORM_TYPE: " + vo.getTransformType());
                        // one name defined right now ArticleTransform
                        try {
                            vo.setTransformName(getElementValue(element, ConfigConstants.TRANSFORM_NAME));
                           
                        } catch (Exception e) {
                            vo.setTransformName("");
                        }
                        /*  not needed remove later
                        if(vo.getTransformName() != null && vo.getTransformName().length() != 0 &&
                        		!vo.getTransformName().equalsIgnoreCase("NA")) {
                        	 throw new Exception("Config:" + vo.getCms() + ":" + ConfigConstants.TRANSFORM_NAME + 
                             		" not supported at this time.  Please use " + ConfigConstants.TRANSFORM_TYPE);
                        }
                        */
                        logger.debug("TRANSFORM_NAME: " + vo.getTransformName());
                        subfolders.add(i, vo);
                    }
                }
                return Boolean.TRUE;
            }
        }catch (Exception e) {
            String msg = Main.getLogExceptionMsg(e, "Error reading configuration file :: "+ configFileName+"\n");
            logger.fatal(msg);
            Main.shutdownApplication(1);
        }
        return Boolean.FALSE;
    }

    /**
     *  Cycle through all subfolders read from the XML file
     *  and create a corresponding entry into the folderSettingsArrayList
     */
    private void initFolderSettings() {
        folderSettingsArrayList = new ArrayList<Folder2ExistSettings>();

        for(SrcExistVO info : subfolders){
            Folder2ExistSettings f2es =
                    new Folder2ExistSettings(info.getCms(), info.isRecurse(), info.getServer(), info.getPort(), info.getExist(),
                                             info.isUseSSL(), info.getUname(), info.getPword(), info.getTransformType(), info.getTransformName());
            folderSettingsArrayList.add(f2es);
        }
    }

    public List<Folder2ExistSettings> getFolderSettingsArrayList() {
        return folderSettingsArrayList;
    }

    public long getApplicationSleepTime() {
        return applicationSleepTime;
    }

    public long getFileChangeSleepTime() {
        return fileChangeSleepTime;
    }

    public String getAppName() {
        return appName;
    }

    public String getLogFileDir() {
        return logFileDir;
    }
    public long getHeartbeatLoopCounter() {
          return heartbeatLoopCounter;
     }

     public void setHeartbeatLoopCounter(long heartbeatLoopCounter) {
          this.heartbeatLoopCounter = heartbeatLoopCounter;
     }
     private static String getElementValue(Element element, String key){
          NodeList serverNodeList = element.getElementsByTagName(key);
          Element serverNodeElement = (Element) serverNodeList.item(0);
          NodeList serverNode = serverNodeElement.getChildNodes();

          // for debugging
          //System.out.println("getElementValue:" + serverNode.item(0).getNodeValue());
          return serverNode.item(0).getNodeValue();
     }

    private class SrcExistVO{
        private String cms;
        private boolean recurse;
        private String exist;
        private String server;
        private int port;
        private boolean useSSL;
        private String uname;
        private String pword;
        private String httpMethod;
        private String transformType;
        private String transformName;

        public String getTransformName() {
            return transformName;
        }

        public void setTransformName(String transformName) {
            this.transformName = transformName;
        }

        public String getTransformType() {
            return transformType;
        }

        public void setTransformType(String transformType) {
            this.transformType = transformType;
        }

        public String getCms() {
            return cms;
        }

        public void setCms(String cms) {
            this.cms = cms;
        }

        public String getExist() {
            return exist;
        }

        public void setExist(String exist) {
            this.exist = exist;
        }

        public String getHttpMethod() {
            return httpMethod;
        }

        public void setHttpMethod(String httpMethod) {
            this.httpMethod = httpMethod;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        public String getPword() {
            return pword;
        }

        public void setPword(String pword) {
            this.pword = pword;
        }

        public String getServer() {
            return server;
        }

        public void setServer(String server) {
            this.server = server;
        }

        public String getUname() {
            return uname;
        }

        public void setUname(String uname) {
            this.uname = uname;
        }

        public boolean isUseSSL() {
            return useSSL;
        }

        public void setUseSSL(boolean useSSL) {
            this.useSSL = useSSL;
        }

		public boolean isRecurse() {
			return recurse;
		}

		public void setRecurse(boolean recurse) {
			this.recurse = recurse;
		}
        
    }//end private inner class
}